package com.mimose.component.chains.config;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.mimose.component.chains.chain.Chain;
import com.mimose.component.chains.util.Constants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description 扫描链路
 * @Author ccy
 * @Date 2019/12/11
 */
@Component
@Order(value=0)
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ChainDog implements CommandLineRunner, ResourceLoaderAware {

    private final ChainStorage chainStorage;

    private static final Map<String, Class<?>> CHAIN_MAP = Maps.newHashMap();

    private static ExecutorService executorService;

    private ResourceLoader resourceLoader;

    /**
     * 初始化参数信息
     */
    private void beforeBuild(){
        ChainExecutorProperties chainExecutorProperties = chainStorage.getChainExecutorProperties();
        executorService = new ThreadPoolExecutor(
                chainExecutorProperties.getCorePoolSize(),
                chainExecutorProperties.getMaximumPoolSize(),
                chainExecutorProperties.getKeepAliveTime(),
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(chainExecutorProperties.getQueueCapacity()),
                new ThreadFactoryBuilder().build(),
                new ThreadPoolExecutor.AbortPolicy());
    }

    @Override
    public void run(String... args) throws Exception {
        beforeBuild();
        // 扫描元数据
        ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        MetadataReaderFactory cachingReader = new CachingMetadataReaderFactory(resourceLoader);
        Resource[] resources = resolver.getResources("classpath*:**/*hain*.class");
        String chainSubName = Chain.class.getName();
        Arrays.stream(resources).filter(resource -> {
            try {
                // 扫描到Chain的子类
                return chainSubName.equals(cachingReader.getMetadataReader(resource).getClassMetadata().getSuperClassName());
            } catch (IOException e) {
                return false;
            }
        }).forEach(resource -> {
            try {
                // 初始化链路
                MetadataReader reader = cachingReader.getMetadataReader(resource);
                Class<?> aClass = Class.forName(reader.getClassMetadata().getClassName());
                Chain chain = Class.forName(reader.getClassMetadata().getClassName()).asSubclass(Chain.class).newInstance();
                log.info("添加链路标识[{}]和链路类[{}]的映射", chain.myName(), aClass.getName());
                CHAIN_MAP.put(chain.myName(), aClass);
            } catch (IOException | ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 根据标识获取链路
     * @param chainName
     * @return
     */
    public static Chain newChain0(String chainName) {
        try{
            Class<?> clazz = null;
            if(CHAIN_MAP == null || !CHAIN_MAP.containsKey(chainName)){
                log.info("Chain is null, return to default chain");
                clazz = CHAIN_MAP.get(Constants.DEFAULT_CHAIN_NAME);
            }else{
                clazz = CHAIN_MAP.get(chainName);
            }
            return clazz!=null? Chain.class.cast(clazz.newInstance()):null;
        }catch (final Exception e){
            log.error("newChain0 error", e);
            return null;
        }
    }

    /**
     * 获取线程池
     * @return
     */
    public static ExecutorService wantExecutor(){
        if(ObjectUtils.isEmpty(executorService)){
            throw new RuntimeException("ExecutorService is empty");
        }
        return executorService;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}
